#include "ws2812b.h"

struct rt_spi_device *spi_ws2812;
static rt_thread_t led_refresh_thread = RT_NULL;
static rt_sem_t led_refresh_sem = RT_NULL; //刷新标志
// uint16_t         *ascii = NULL;         //ASCII字模
const static uint16_t ascii[] =
    {
        0X0000, 0X2482, 0X5A00, 0X5F7D, 0X2FBA, 0X188C, 0X78EF, 0X2400, 0X2922, 0X224A,
        0X0AA8, 0X05D0, 0X0014, 0X01C0, 0X0004, 0X02A0, 0X7B6F, 0X2C97, 0X73E7, 0X72CF,
        0X5BC9, 0X79CF, 0X79EF, 0X7249, 0X7BEF, 0X7BCF, 0X0410, 0X0414, 0X0510, 0X0E38,
        0X08A0, 0X7282, 0X0551, 0X2BED, 0X6BAE, 0X3923, 0X6B6E, 0X79A7, 0X79A4, 0X792F,
        0X5BED, 0X7497, 0X7496, 0X5BAD, 0X4927, 0X5F6D, 0X6B6D, 0X2B6A, 0X6BA4, 0X3AC9,
        0X6BB5, 0X39CE, 0X7492, 0X5B6F, 0X5B6A, 0X5B7D, 0X5AAD, 0X5A92, 0X72A7, 0X6926,
        0X0888, 0X324B, 0X0540, 0X0007, 0X2200, 0X2BED, 0X6BAE, 0X3923, 0X6B6E, 0X79A7,
        0X79A4, 0X792F, 0X5BED, 0X7497, 0X7496, 0X5BAD, 0X4927, 0X5F6D, 0X6B6D, 0X2B6A,
        0X6BA4, 0X3AC9, 0X6BB5, 0X39CE, 0X7492, 0X5B6F, 0X5B6A, 0X5B7D, 0X5AAD, 0X5A92,
        0X72A7, 0X3593, 0X0490, 0X64D6, 0X03E0};
static void led_init(void);
static void led_refresh(void);
static void led_refresh_thread_entry(void *parameter);
static void WS_buildData(WS_Color color, uint8_t *p);

WS_Color CanvasBuffer[WIDTH * HEIGHT]; //刷新缓冲区
static uint8_t ws_buffer[WS_CANVAS_SIZE];
WS_Color ForegroundColor = {0xFF, 0xFF, 0xFF, TRUE};  //前景色
WS_Color BackgroundColor = {0x00, 0x00, 0x00, FALSE}; //背景色

WS_Window Window = {0, 0, WIDTH, HEIGHT}; // 开窗位置
uint8_t Brightness = 0xa8;//0xFF;                // 亮度
uint8_t Transparent = 0xFF;               // 透明度

/**
 * @brief 将RGB写入到目标地址中,顺序为GRB，构造成SPI格式数据
          亮度需要在这里进行设置
 * @param color 颜色值
 * @param *p  要写入数组的坐标
 */
static void WS_buildData(WS_Color color, uint8_t *p)
{
  uint8_t b;
  b = color.Green * (Brightness / 255.0);
  for (int i = 7; i >= 0; i--)
  {
    *p = ((0x01 & (b >> i)) == 1) ? BITE_1 : BITE_0;
    p++;
  }
  b = color.Red * (Brightness / 255.0);
  for (int i = 7; i >= 0; i--)
  {
    *p = ((0x01 & (b >> i)) == 1) ? BITE_1 : BITE_0;
    p++;
  }
  b = color.Blue * (Brightness / 255.0);
  for (int i = 7; i >= 0; i--)
  {
    *p = ((0x01 & (b >> i)) == 1) ? BITE_1 : BITE_0;
    p++;
  }
}

/**
 * @brief 配置WS2812B的SPI控制
 */
void spi_ws2812b_config(void)
{
  struct rt_spi_device *spi_ws2812;
  spi_ws2812 = (struct rt_spi_device *)rt_device_find("ws_led");
  if (spi_ws2812)
  {
    // 找到设备，配置设备
    //        rt_kprintf("Find SPI10\n");
    struct rt_spi_configuration cfg;
    cfg.data_width = 8;
    cfg.max_hz = 6 * 1000 * 1000;                          // 6MHz，如果用的视频SPI1就用6MHz，SPI2要加倍，所以是12MHz
    cfg.mode = RT_SPI_MASTER | RT_SPI_MODE_1 | RT_SPI_MSB; // RT_SPI_MODE_0刷新第一像素有问题

    rt_spi_configure(spi_ws2812, &cfg);
  }
  else
  {
    rt_kprintf("Not find SPI20\n");
  }
}

/**
 * 刷新LED灯板内容
 * 将内容发送到SPI中
 */
static void led_refresh(void)
{

  spi_ws2812 = (struct rt_spi_device *)rt_device_find("ws_led");
  if (spi_ws2812)
  {
    
    //前面的复位信号
    for (int i = 0; i < RES_WIDTH; i++)
    {
      ws_buffer[i] = 0;
    }
    //正式数据
    uint16_t size = WIDTH * HEIGHT;
    for (int i = 0; i < size; i++)
    {
      WS_buildData(CanvasBuffer[i], ws_buffer + RES_WIDTH + i * 24);
    }
    rt_enter_critical();
    rt_spi_transfer(spi_ws2812, ws_buffer, RT_NULL, WS_CANVAS_SIZE);
    rt_exit_critical();
  }
}
/**
 * LED刷新线程
 * 等待信号到达，信号到达后开始处理
 * 该线程的优先级最高
 */
static void led_refresh_thread_entry(void *parameter)
{
  rt_err_t result;
  rt_kprintf(" led_refresh thread start\n");
  while (1)
  {
    result = rt_sem_take(led_refresh_sem, RT_WAITING_FOREVER);
    if (RT_EOK == result)
    {
      //刷新
      led_refresh();
      led_refresh_sem->value = 0; //清空所有信号
    }
  }
}
char nhbuff[32] =
{
  0x08, 0x18, 0x30, 0x7F, 0xFF, 0xD6, 0xBE, 0x7D,
  0xE9, 0xFF, 0x3F, 0x28, 0x28, 0x2C, 0x26, 0x24,
  0x00, 0x21, 0x2D, 0x76, 0x24, 0x3A, 0x2A, 0x08,
  0x48, 0x49, 0x5F, 0x68, 0x48, 0x48, 0x00, 0x00
}; /*"你好",0*/

char jybuff[32] =
{
  0x09, 0x19, 0x15, 0x15, 0x35, 0x37, 0x77, 0xFF, 
  0xDF, 0xDF, 0x75, 0x37, 0x37, 0x3D, 0x1D, 0x15, 
  0x11, 0x11, 0x00, 0x00, 0x01, 0x02, 0x06, 0x7C, 
  0x68, 0x68, 0x68, 0x68, 0x69, 0x69, 0x69, 0x7E
};

unsigned char mdbuf[32] = 
{0x00,0x00,0x7E,0x69,0x6A,0x6A,0x68,0x50,0x6C,0x6C,0x6A,0x6A,0x29,0x09,0x01,0x00,
0x00,0x01,0x21,0x21,0x22,0x22,0x24,0xF8,0x10,0x28,0x24,0x22,0x22,0x21,0x20,0x00};

unsigned char qrbuf[32]= 
{0x00,0x50,0x50,0x60,0x5F,0x54,0x54,0xEA,0x54,0x54,0x55,0x5F,0x50,0x50,0x00,0x00,
0x00,0x00,0x34,0x6C,0x3F,0x2C,0x49,0x11,0x22,0x64,0x38,0x24,0x22,0x21,0x20,0x00};

unsigned char ywbuf[32]= 
{0x00,0x02,0x7C,0x40,0x41,0x51,0x4D,0x41,0xD1,0x49,0x42,0x45,0x59,0x52,0x00,0x00,
0x00,0x14,0x64,0x68,0x7F,0x28,0x34,0x2A,0x4A,0x34,0x24,0x39,0x21,0x26,0x38,0x00};

void dn_wr(char *buff, int len, WS_Color cr)
{
  //WS_Clear();
 
  char ch = 0;
  for (int i = 0; i < len; i++)
  {
    ch = buff[i];

    for (int j = 0; j < 8; j++)
    {
      //rt_kprintf("ch:0X%0X\n", ch);
      char a = ch & 0x80;
      // rt_kprintf("a:0X%0X\n",a);
      if (a)
      {
        WS_DrawPoint(i, j, cr);
      }
      else
      {
        WS_DrawPoint(i, j, COLOR_BLACK);
      }
      ch = ch << 1;

      // rt_thread_mdelay(1000);
    }
  }
   WS_Show();
}

int switchstatus = 0;
extern rt_uint32_t dhtemp;
extern rt_uint32_t dhhumi;

char show_status = 0;
unsigned char enbuff[12]={0};

void wifi_led_contrl(unsigned char *buff)
{
  switch(buff[0])
  {
    case '0':
      switchstatus = 0;
    break;
    case '1':
      switchstatus = 1;
      show_status =1;
    break;
    case '2':
      switchstatus = 1;
      show_status = 2;
    break;
    case '3':
      switchstatus = 1;
      show_status = 3;
    break;
    case '4':
      switchstatus = 1;
      show_status = 4;
    break;
    case '5':
      switchstatus = 1;
      show_status = 5;
    break;
    case '6':
      switchstatus = 1;
      show_status = 6;
    break;
    case '7':
      switchstatus = 1;
      show_status = 7;

      rt_memcpy((void *)enbuff,(const void *)&buff[1],8);
      rt_kprintf("enbuff:%s\n",enbuff);
    break;
    case '8':
        rt_kprintf("wifi get time:%d/%d/%d %d:%d:%d\n",
        buff[1],buff[2],buff[3],buff[4],buff[5],buff[6],buff[7]);
    
        int ret = set_date(2000+buff[1],buff[2],buff[3]);
        if (ret != RT_EOK)
        {
            rt_kprintf("set RTC date failed\n");
            return ret;
        }
        ret =set_time(buff[4],buff[5],buff[6]);
         if (ret != RT_EOK)
        {
            rt_kprintf("set RTC time failed\n");
            return ret;
        }
     
    break;
    default:
    break;
  }
  rt_kprintf("switchstatus:%d,show_status:%d\n",switchstatus,show_status);
}

static void led_show_thread_entry(void *parameter)
{
  rt_kprintf("led_show thread start\n");
  rt_thread_mdelay(2000);
  int i = 0;
  char t[20];
  struct tm timesl;
  time_t now;
  //char show_status = 0;
  //rt_kprintf("switchstatus:%d\n",switchstatus);
  //rt_kprintf("show_status:%d\n",show_status);
  while (1)
  {
    rt_kprintf("switchstatus:%d\n",switchstatus);
    rt_kprintf("show_status:%d\n",show_status);
    if (!switchstatus)
    {
      WS_Clear();
      //i = 10;
      //show_status = 1;
      //rt_thread_mdelay(500);
    }
    else
    {
      //rt_kprintf(" i:%d\n",i);
      // if (i % 10 == 0)
      // {
      //   i = 0;
      //   rt_memset(t,0,20);      
      //   show_status++;   
      // }
      i++;
      
      switch (show_status)
      {
        case 1:
          now = time(NULL);
          localtime_r(&now, &timesl);
          sprintf(t, "%02d/%02d/%02d", timesl.tm_year-100, timesl.tm_mon+1, timesl.tm_mday);
         
          WS_Erase();
          WS_SetForegroundColor(COLOR_YELLOR);
          WS_DrawRectangle(0, 0, 32, 8);
          WS_SetForegroundColor(COLOR_BLUE);
          WS_DrawString(1, 2, t, TRUE);
          rt_kprintf("led show:%s\n", t);
        break;
        case 2:
          now = time(NULL);
          localtime_r(&now, &timesl);
          sprintf(t, "%02d:%02d:%02d", timesl.tm_hour, timesl.tm_min, timesl.tm_sec); 
          WS_Erase();
          WS_SetForegroundColor(COLOR_GREEN);
          WS_DrawRectangle(0, 0, 32, 8);
          WS_SetForegroundColor(COLOR_BLUE);
          WS_DrawString(2, 2, t, TRUE);
          rt_kprintf("led show:%s\n", t);
        break;
        case 3:
          sprintf(t, "T:%02d H:%02d", dhtemp/10, dhhumi/10);
          WS_Erase();
          WS_SetForegroundColor(COLOR_RED);
          WS_DrawRectangle(0, 0, 32, 8);
          WS_SetForegroundColor(COLOR_BLUE);
          WS_DrawString(2, 2, t, TRUE);
          rt_kprintf("led show:%s\n", t);    
        break;
        case 4:
          dn_wr(mdbuf, 32, COLOR_YELLOR);
        break;
        case 5:
          dn_wr(qrbuf, 32, COLOR_YELLOR);
        break;
        case 6:
          dn_wr(ywbuf, 32, COLOR_YELLOR);
        break;
        case 7:
          WS_Erase();
          WS_DrawString(1, 2, enbuff, TRUE);
        break;
        default:
        break;
      }
      
      WS_Show();
    }
    rt_thread_mdelay(500);
  }
}
static void led_init(void)
{
  //    led_refresh_mutex   =   rt_mutex_create("led_refresh" , RT_IPC_FLAG_FIFO);
  led_refresh_sem = rt_sem_create("led_refresh", 1, RT_IPC_FLAG_FIFO);
  led_refresh_thread = rt_thread_create("led_refresh", led_refresh_thread_entry, RT_NULL, 512, 2, 10);
  rt_thread_startup(led_refresh_thread); //启动线程

  rt_thread_t led_show_thread = rt_thread_create("led_show", led_show_thread_entry, RT_NULL, 8192, 10, 10);
  rt_thread_startup(led_show_thread); //启动线程
}

/**
 * 初始化WS2812LED
 */
void spi_ws2812b_init(void)
{
  // 注册设备
  //__HAL_RCC_GPIOB_CLK_ENABLE();
  RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_GPIOB, ENABLE);
  // 必须制定一个片选信号线，否则注册不成功
  rt_hw_spi_device_attach("spi2", "ws_led", GPIOB, GPIO_PIN_12);
  led_init();
}

int ws2812_led_init(void)
{
  spi_ws2812b_init();
  spi_ws2812b_config();
  return RT_EOK;
}
//自动初始化
// INIT_COMPONENT_EXPORT(rt_ws2812_led_init);
MSH_CMD_EXPORT(ws2812_led_init, ws2812_led_init);

void spi_ws2812_test()
{
  char buff[24];
  for (int i = 0; i < 24; i++)
  {
    buff[i] = BITE_1;
  }
  rt_spi_transfer(spi_ws2812, buff, RT_NULL, 24);
}
MSH_CMD_EXPORT(spi_ws2812_test, spi_ws2812_test)
/**
 * 刷新LED灯板内容
 * 重置互斥信号量
 */
void WS_Show(void)
{
  //rt_kprintf("wsled show.\n");
  rt_sem_release(led_refresh_sem);
}

/**
 * @brief 设置LED灯的整体亮度。
 * @param V 整体亮度
 */
void WS_SetBrightness(uint8_t V)
{
  Brightness = V;
}

/**
 * @brief 设置前景色。
 * @param Color 前景色
 */
void WS_SetForegroundColor(WS_Color Color)
{
  ForegroundColor.Red = Color.Red;
  ForegroundColor.Green = Color.Green;
  ForegroundColor.Blue = Color.Blue;
  ForegroundColor.Transparent = Color.Transparent;
}

/**
 * @brief 设置背景色。
 * @param Color 背景色
 */
void WS_SetBackgroundColor(WS_Color Color)
{
  BackgroundColor.Red = Color.Red;
  BackgroundColor.Green = Color.Green;
  BackgroundColor.Blue = Color.Blue;
  BackgroundColor.Transparent = Color.Transparent;
}

/**
 * @brief 设置绘制前景时的透明度，和当前位置像素值进行比值+运算，在当前比值基础上加上百分比的颜色
 * @param V 整体亮度
 */
void WS_SetTransparent(uint8_t V)
{
  Transparent = V;
}

/**
 * @brief 开窗，开窗后在窗口内绘制，超出窗口的不进行绘制
 * @param X 开窗相对于屏幕的X轴位置
 * @param Y 开窗相对于屏幕的Y轴位置
 * @param Width 开窗宽度
 * @param Height 开窗高度
 */
void WS_OpenWindow(int16_t X, int16_t Y, uint16_t Width, uint16_t Height)
{
  Window.X = X;
  Window.Y = Y;
  Window.Width = Width;
  Window.Height = Height;
}

/**
 * @brief 取消开窗，恢复到原来绘制区域大小
 */
void WS_CloseWindow(void)
{
  Window.X = 0;
  Window.Y = 0;
  Window.Width = WIDTH;
  Window.Height = HEIGHT;
}

/**
 * @brief 清空缓冲区的内容，并重绘到屏幕上
 */
void WS_Clear(void)
{
  rt_kprintf("wsled clear.\n");
  for (int i = 0; i < (sizeof(CanvasBuffer) / sizeof(CanvasBuffer[0])); i++)
  {
    // rt_kprintf("i:%d\n",i);
    CanvasBuffer[i] = BackgroundColor;
  }
  // rt_kprintf("wsled clear...\n");
  WS_Show();
}

/**
 * @brief 擦除开窗位置为背景色，不会重绘到屏幕上
 */
void WS_Erase(void)
{
  for (int x = 0; x < Window.Width; x++)
    for (int y = 0; y < Window.Height; y++)
      WS_DrawPoint(x, y, BackgroundColor);
}
/**
 * @brief 在指定位置上画一个点
          透明度在此设置，透明度/255是当前要绘制点的透明度
          (255-透明度)/255是原来的透明度，两者相加
 * @param X 相对于开窗的X轴坐标
 * @param Y 相对于开窗的Y轴坐标
 * @param Color 绘制的颜色
 */
void WS_DrawPoint(int16_t X, int16_t Y, WS_Color Color)
{
  int16_t x = X + Window.X;
  int16_t y = Y + Window.Y;
  if (x >= 0 && x < Window.X + Window.Width && x < WIDTH &&
      y >= 0 && y < Window.Y + Window.Height && y < HEIGHT)
  {
    // int index = y*WIDTH+x;
    int index = 0; //@丁宁 适配led屏显示
    if (x % 2 == 0)
    {
      index = x * HEIGHT + y;
    }
    else
    {
      index = x * HEIGHT + HEIGHT - y - 1;
    }

    float r = (255 - Transparent) / 255.0;
    float d = Transparent / 255.0;
    CanvasBuffer[index].Red = CanvasBuffer[index].Red * r + Color.Red * d;
    CanvasBuffer[index].Green = CanvasBuffer[index].Green * r + Color.Green * d;
    CanvasBuffer[index].Blue = CanvasBuffer[index].Blue * r + Color.Blue * d;
  }
}

/**
 * @brief 在开窗的位置进行划线，当超出开窗后不进行绘制
 * @param X1  起始点X轴位置
 * @param Y1  起始点Y周位置
 * @param X2  结束点X轴位置
 * @param Y2  结束点Y周位置
 */
void WS_DrawLine(int16_t X1, int16_t Y1, int16_t X2, int16_t Y2)
{
  int16_t increaseX, increaseY, distance;
  int16_t errorX = 0, errorY = 0;
  /* 计算X轴和Y轴的增量坐标 */
  int16_t deltaX = X2 - X1;
  int16_t deltaY = Y2 - Y1;

  /* 绘图时当前点位置 */
  int16_t currentX = X1;
  int16_t currentY = Y1;

  /* 计算两个轴的绘制增量 */
  if (deltaX > 0)
    increaseX = 1;
  else if (0 == deltaX)
    increaseX = 0;
  else
  {
    increaseX = -1;
    deltaX = -deltaX;
  }

  if (deltaY > 0)
    increaseY = 1;
  else if (0 == deltaY)
    increaseY = 0;
  else
  {
    increaseY = -1;
    deltaY = -deltaY;
  }

  /* 选择绘制的方向 */
  if (deltaX > deltaY)
    distance = deltaX;
  else
    distance = deltaY;

  /* 绘制点 */
  for (int i = 0; i <= distance + 1; i++)
  {
    /* 判断是否在开窗内，如果超出开窗返回则不绘制 */
    WS_DrawPoint(currentX, currentY, ForegroundColor);
    errorX += deltaX;
    errorY += deltaY;
    if (errorX > distance)
    {
      errorX -= distance;
      currentX += increaseX;
    }
    if (errorY > distance)
    {
      errorY -= distance;
      currentY += increaseY;
    }
  }
}

/**
 * @brief 在开窗的位置画一个矩形边框，当超出开窗后不进行绘制
 * @param X       起始点X轴位置
 * @param Y       起始点Y周位置
 * @param Width   矩形宽度
 * @param Height  矩形高度
 */
void WS_DrawRectangle(int16_t X, int16_t Y, uint16_t Width, uint16_t Height)
{
  WS_DrawLine(X, Y, X + Width - 1, Y);
  WS_DrawLine(X, Y + Height - 1, X + Width - 1, Y + Height - 1);
  WS_DrawLine(X, Y, X, Y + Height - 1);
  WS_DrawLine(X + Width - 1, Y, X + Width - 1, Y + Height - 1);
}

/**
 * @brief 在开窗的位置画一个实心矩形，当超出开窗后不进行绘制
 * @param X       起始点X轴位置
 * @param Y       起始点Y周位置
 * @param Width   矩形宽度
 * @param Height  矩形高度
 */
void WS_FillRectangle(int16_t X, int16_t Y, uint16_t Width, uint16_t Height)
{
  for (int x = 0; x < Width; x++)
  {
    for (int y = 0; y < Height; y++)
    {
      WS_DrawPoint(X + x, Y + y, ForegroundColor);
    }
  }
}

/**
 * @brief 在开窗的位置画一个圆形边框，当超出开窗后不进行绘制
 * @param X       圆中心点X坐标
 * @param Y       圆中心点Y坐标
 * @param Radius  半径
 */
void WS_DrawCircle(int16_t X, int16_t Y, uint16_t Radius)
{
  Radius -= 1;
  /* 绘图时当前点位置 */
  int16_t currentX, currentY;
  int16_t error = 0;
  currentX = 0;
  currentY = Radius;
  error = 3 - (Radius << 1);

  while (currentX <= currentY)
  {
    WS_DrawPoint(X + currentX, Y + currentY, ForegroundColor); // 1
    WS_DrawPoint(X - currentX, Y + currentY, ForegroundColor); // 2
    WS_DrawPoint(X - currentY, Y + currentX, ForegroundColor); // 3
    WS_DrawPoint(X - currentY, Y - currentX, ForegroundColor); // 4
    WS_DrawPoint(X - currentX, Y - currentY, ForegroundColor); // 5
    WS_DrawPoint(X + currentX, Y - currentY, ForegroundColor); // 6
    WS_DrawPoint(X + currentY, Y - currentX, ForegroundColor); // 7
    WS_DrawPoint(X + currentY, Y + currentX, ForegroundColor); // 0
    currentX++;
    if (error < 0)
      error += 4 * currentX + 6;
    else
    {
      error += 10 + 4 * (currentX - currentY);
      currentY--;
    }
  }
}

/**
 * @brief 在开窗的位置画一个圆形边框，当超出开窗后不进行绘制
 * @param X       圆中心点X坐标
 * @param Y       圆中心点Y坐标
 * @param Radius  半径
 */
void WS_FillCircle(int16_t X, int16_t Y, uint16_t Radius)
{
  Radius -= 1;
  /* 绘图时当前点位置 */
  int16_t currentX, currentY;
  int16_t error = 0;
  currentX = 0;
  currentY = Radius;
  error = 3 - (Radius << 1);

  while (currentX <= currentY)
  {
    for (int16_t countY = currentX; countY <= currentY; countY++)
    {
      WS_DrawPoint(X + currentX, Y + countY, ForegroundColor); // 1，研究对象
      WS_DrawPoint(X - currentX, Y + countY, ForegroundColor); // 2
      WS_DrawPoint(X - countY, Y + currentX, ForegroundColor); // 3
      WS_DrawPoint(X - countY, Y - currentX, ForegroundColor); // 4
      WS_DrawPoint(X - currentX, Y - countY, ForegroundColor); // 5
      WS_DrawPoint(X + currentX, Y - countY, ForegroundColor); // 6
      WS_DrawPoint(X + countY, Y - currentX, ForegroundColor); // 7
      WS_DrawPoint(X + countY, Y + currentX, ForegroundColor); // 0
    }
    currentX++;
    if (error < 0)
      error += 4 * currentX + 6;
    else
    {
      error += 10 + 4 * (currentX - currentY);
      currentY--;
    }
  }
}

/**
 * @brief 加载字库文件
 */
static void WS_LoadFount(void)
{
  // int ascii_size=190;
  // int f = open("/sys/font/ascii.mod",O_RDONLY);
  // if(f!=-1)
  // {
  //     ascii = rt_malloc(ascii_size); //分配190字节的空间
  //     read(f,ascii,ascii_size);
  //     close(f);
  // }
}

/**
 * @brief 以左上角为基准点，绘制一个字符，字高5像素，宽3像素
 * @param X       圆中心点X坐标
 * @param Y       圆中心点Y坐标
 * @param C       打印内容
 * @param Compact 是否紧凑输出，TRUE表示紧凑输出，FALSE表示正常输入
 *                紧凑输出的时候，对于字间只有一像素的空格，
 *                非紧凑输出的时候按照每个字符3像素宽度输出
 *                当输出超出开窗时不进行输出
 * @return 占用宽度
 */
int WS_DrawChar(int16_t X, int16_t Y, char C, Boolean Compact)
{
  int w = 3;
  if (ascii == NULL)
  {
    WS_LoadFount();
  }

  uint16_t c = ascii[C - 32]; //取出字模
  if (Compact == TRUE)
  {
    /*
     * 紧凑输出
     * 判断左右是否为空
     */
    if ((0x4924 & c) == 0)
    {
      //左边一列没有数据
      X--;
      w--;
    }
    if ((0x2492 & c) == 0)
    {
      //中间一列没有数据=
      X--;
      w--;
    }
    if ((0x1249 & c) == 0)
    {
      //右边一列没有数据
      w--;
    }
  }
  for (int y = 0; y < 5; y++)
  {
    for (int x = 0; x < 3; x++)
    {
      if (((0x4000 >> (y * 3 + x)) & c) > 0)
      {
        //绘制
        WS_DrawPoint(X + x, Y + y, ForegroundColor);
      }
    }
  }
  return w;
}

/**
 * @brief 以左上角为基准点，绘制文字，字高5像素，宽3像素
 * @param X       圆中心点X坐标
 * @param Y       圆中心点Y坐标
 * @param Text    打印内容
 * @param Compact 是否紧凑输出，TRUE表示紧凑输出，FALSE表示正常输入
 *                紧凑输出的时候，对于字间只有一像素的空格，
 *                非紧凑输出的时候按照每个字符3像素宽度输出
 *                当输出超出开窗时不进行输出
 * @return 字符串宽度
 */
int WS_DrawString(int16_t X, int16_t Y, char *Text, Boolean Compact)
{
  char c = 0;
  int i = 0;
  int16_t x = X;
  while ((c = *(Text + i)) != 0)
  {
    x += WS_DrawChar(x, Y, c, Compact);
    x += 1; //补一个空格
    i++;
  }
  return x > 0 ? x - 1 : x;
}

/**
 * @brief 以左上角为基准点，绘制图片
 * img类型图片格式：
 *      字节1:     图片类型，1灰度图，2RGB24位色，3RGB565，最高位透明度：0x80：透明，0x00不透明
 *      字节2:     预留（用于颜色表扩展等）
 *      字节3:     图片宽度
 *      字节4:     图片高度
 *      字节5:     配色表长度
 *      色标字节:  3字节为一组，表示RGB色，2字节为一组表示565色，灰度不用颜色（色表长度为0）
 *      像素数据:  每1字节表示一个像素数据，颜色按照色表进行取色
 * @param X        圆中心点X坐标
 * @param Y        圆中心点Y坐标
 * @param Image    图片指针
 */
void WS_DrawImage(int16_t X, int16_t Y, uint8_t *Image)
{
}

/**
 * @brief 以左上角为基准点，绘制图片
 * @param X        圆中心点X坐标
 * @param Y        圆中心点Y坐标
 * @param Width    图片宽度
 * @param Height   图片高度
 * @param Picture  转换成颜色值的图片
 */
// void WS_DrawPicture(int16_t X,int16_t Y，uint16_t Width,uint16_t Height ,WS_Color* Picture)
//{
// }

//泡泡属性
typedef struct __popo
{
  int16_t X;
  int16_t Y;
  uint16_t Size;
  uint16_t Delay;
  WS_Color Color;

} POPO;

//测试泡泡
void test_thread_entry(POPO *p)
{
  while (1)
  {
    rt_enter_critical();
    //擦除上一个泡泡
    WS_SetForegroundColor(COLOR_BLACK);
    WS_DrawCircle(p->X, p->Y + 1, p->Size);

    WS_SetForegroundColor(p->Color);
    WS_DrawCircle(p->X, p->Y, p->Size);
    p->Y = p->Y - 1;
    if (p->Y < -p->Size)
    {
      p->Y = HEIGHT + p->Size;
    }
    rt_thread_delay(p->Delay);
    rt_exit_critical();
    WS_Show();
  }
}
static void text_test(void)
{
  rt_kprintf("开始输出文字测试:\n");
  while (1)
  {
    WS_SetBackgroundColor(COLOR_BLUE);
    WS_Erase();
    WS_SetForegroundColor(COLOR_BLACK);
    int w = WS_DrawString(0, 1, "12345678", TRUE);
    WS_Show();
    //        rt_kprintf("->文字输出,宽度：%d\n",w);
    rt_thread_delay(2000);

    WS_SetBackgroundColor(COLOR_BLACK);
    WS_Erase();
    WS_SetForegroundColor(COLOR_BLUE);
    w = WS_DrawString(0, 1, "12345678", TRUE);
    WS_Show();
    //        rt_kprintf("->文字输出,宽度：%d\n",w);
    rt_thread_delay(2000);
  }
}
void time_test_entry()
{
  rt_kprintf("开始时间输出：\n");
  char t[20];
  static struct tm tm;
  time_t now;
  while (1)
  {
    now = time(NULL);
    gmtime_r(&now, &tm);
    //        sprintf(t,"%02d-%02d %02d:%02d:%02d",tm.tm_mon,tm.tm_mday,tm.tm_hour,tm.tm_min,tm.tm_sec);
    sprintf(t, "%02d:%02d:%02d", tm.tm_hour, tm.tm_min, tm.tm_sec);

    WS_SetForegroundColor(COLOR_BLUE);
    WS_Erase();
    WS_DrawString(3, 1, t, TRUE);
    WS_Show();

    //        rt_kprintf("当前时间：");
    //        rt_kprintf(t);
    //        rt_kprintf("\n");
    rt_thread_mdelay(1000);
  }
}


void led_test()
{
  rt_kprintf("led_test\n");
  WS_Color c;
  
  c = COLOR_BLUE;
  WS_DrawPoint(0, 0, c);
  WS_DrawPoint(1, 0, c);
  WS_DrawPoint(2, 0, c);
  WS_DrawPoint(3, 0, c);
  WS_DrawPoint(4, 0, c);
  WS_DrawPoint(5, 0, c);
  WS_DrawPoint(1, 1, c);
  WS_DrawPoint(2, 2, c);
  WS_DrawPoint(3, 3, c);
  WS_DrawPoint(4, 4, c);
  WS_DrawPoint(5, 5, c);
  WS_Show();

  WS_Clear();
  for (int i = 0; i < 256; i++)
  {
    WS_DrawPoint(i % 32, i / 32, COLOR_BLUE);
    WS_Show();
    rt_thread_mdelay(10);
  }

  // POPO p;
  // p.X = 5;
  // p.Y = 5;
  // p.Size = 3;
  // p.Delay = 100;
  // p.Color = COLOR_RED;
  // // test_thread_entry(&p);

  // WS_Clear();
  // WS_SetForegroundColor(COLOR_RED);
  // WS_DrawRectangle(1, 1, 5, 5);

  // WS_SetForegroundColor(COLOR_BLUE);
  // WS_DrawRectangle(8, 1, 5, 5);

  // WS_SetForegroundColor(COLOR_YELLOR);
  // WS_FillRectangle(14, 1, 6, 6);

  WS_SetForegroundColor(COLOR_RED);
  WS_FillCircle(25, 4, 2);
  WS_SetForegroundColor(COLOR_BLUE);
  WS_DrawCircle(25, 4, 4);
  WS_Show();
  WS_Clear();

  // text_test();
  time_test_entry();
}
MSH_CMD_EXPORT(led_test, led_test)
